package healthylife.top.sync;

import cn.hutool.core.io.IoUtil;
import cn.hutool.json.JSONUtil;
import io.minio.*;
import io.minio.messages.Item;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author maomao
 */
@Slf4j
@Data
public class MinioSyncClient implements FileSyncService {
    private static final String MINIO_ENDPOINT = System.getenv("MINIO_ENDPOINT");
    private static final String MINIO_ACCESS_KEY = System.getenv("MINIO_ACCESS_KEY");
    private static final String MINIO_SECRET_KEY = System.getenv("MINIO_SECRET_KEY");
    public static MinioClient minioClient;
    public static Map<String, FileInfo> sourceFilesMap;
    public Map<String, FileInfo> lastTransferMap;
    String sourceBucket;
    String targetBucket;
    String tempLocalPath;
    String workspace;

    MinioSyncClient(String sourceBucket, String targetBucket, String tempLocalPath, String workspace) {
        this.sourceBucket = sourceBucket;
        this.targetBucket = targetBucket;
        this.tempLocalPath = tempLocalPath;
        this.workspace = workspace;
        minioClient = MinioClient.builder()
                .endpoint(MINIO_ENDPOINT)
                .credentials(MINIO_ACCESS_KEY, MINIO_SECRET_KEY)
                .build();
    }

    public String getConf() {
        return getWorkspace() + File.separator + "sync-s3.conf";
    }

    public String getLog(String time) {
        if (time == null) {
            time = LocalDateTime.now().toString();
        }
        return getWorkspace() + File.separator + "sync-s3-" + time + ".log";
    }

    @Override
    public String getSource() {
        return this.sourceBucket;
    }

    @Override
    public String getTarget() {
        return this.targetBucket;
    }


    @Override
    @SneakyThrows
    public List<FileInfo> sourceFiles() {
        // 列出 MinIO Bucket 中的所有文件
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(this.sourceBucket).recursive(true).build());
        List<FileInfo> files = new ArrayList<>();
        for (Result<Item> result : results) {
            Item item = result.get();
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFilePath(item.objectName());
            fileInfo.setDirectory(item.isDir());
            fileInfo.setFileId(item.etag());
            fileInfo.setSize(item.size());
            fileInfo.setLastModifiedTime(item.lastModified().toInstant().toEpochMilli());
            files.add(fileInfo);
        }
        sourceFilesMap = files.stream().collect(Collectors.toMap(FileInfo::getFilePath, Function.identity()));
        return files;
    }


    @SneakyThrows
    @Override
    public List<FileInfo> targetFiles() {
        // 列出 MinIO Bucket 中的所有文件
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(this.targetBucket).recursive(true).build());
        List<FileInfo> files = new ArrayList<>();
        for (Result<Item> result : results) {
            Item item = result.get();
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFilePath(item.objectName());
            fileInfo.setDirectory(item.isDir());
            fileInfo.setLastModifiedTime(item.lastModified().toInstant().toEpochMilli());
            files.add(fileInfo);
        }
        return files;
    }

    @Override
    @SneakyThrows
    public int transfer(String source, String target) {
        //先下载到本地temp路径然后再传输到target
        String tempLocalFilePath = tempLocalPath + File.separator + source;
        Path localPath = Paths.get(tempLocalFilePath);
        // 确保本地目录层级存在
        Path parent = localPath.getParent();
        File file = localPath.toFile();
        if (!parent.toFile().exists()) {
            try {
                Files.createDirectories(parent);
                downloadAndUpload(source, tempLocalFilePath);
            } catch (Exception e) {
                throw new RuntimeException(source + "同步失败,code:" + 0 + "error:" + e.getMessage());
            }
            return 0;
        }

        if (file.isDirectory()) {
            return -1;
        }
        if (!sourceFilesMap.containsKey(source)) {
            return -1;
        }
        try {
            //获取来源端的 文件 修改时间
            long lastModifiedTime = sourceFilesMap.get(source).getLastModifiedTime();
            //判断是否需要下载
            int flag = needTransfer(source, lastModifiedTime);
            if (flag >=0) {
                //下载文件到本地目录
                downloadAndUpload(source, tempLocalFilePath);
                return flag;
            }
        } catch (Exception e) {
            throw new RuntimeException(source + "同步失败,code:" + 1 + "error:" + e.getMessage());
        }
        return -1;
    }

    @SneakyThrows
    private void downloadAndUpload(String source, String tempLocalFilePath) {
        downloadObj(source, tempLocalFilePath);
        File file = Paths.get(tempLocalFilePath).toFile();
        FileInputStream fileInputStream = new FileInputStream(file);
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(this.targetBucket)
                        //不能携带桶目录
                        .object(source)
                        .stream(fileInputStream, file.length(), -1)
                        .build());
    }

    @SneakyThrows
    public void downloadObj(String source, String localPath) {
        minioClient.downloadObject(
                DownloadObjectArgs.builder()
                        .bucket(this.sourceBucket)
                        .object(source)
                        .filename(localPath)
                        .build());
        System.out.println("文件下载完成，临时本地存储" + localPath);

    }

    @SneakyThrows
    @Override
    public void deleteFile(String filePath) {
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(targetBucket).object(filePath).build());
    }

    @SneakyThrows
    public int needTransfer(String relativeFilePath, long currentFileLastModifiedTime) {
        if (lastTransferMap == null) {
            Path path = Paths.get(getConf());
            File file = path.toFile();
            if (!file.exists()) {
                log.warn("同步文件配置信息不存在");
                return 0;
            }
            String json = IoUtil.read(Files.newInputStream(file.toPath()), Charset.defaultCharset());
            List<FileInfo> list = JSONUtil.toList(json, FileInfo.class);
            lastTransferMap = list.stream().collect(Collectors.toMap(FileInfo::getFilePath, Function.identity()));
        }
        if (lastTransferMap.containsKey(relativeFilePath)) {
            boolean b = lastTransferMap.get(relativeFilePath).getLastModifiedTime() != currentFileLastModifiedTime;
            if (b) {
                log.info("文件:{}有更新", relativeFilePath);
                return 1;
            } else {
                return -1;
            }
        }
        return 0;

    }


}
